﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Forms;
using GuidanceExplorer.Model;
using GuidanceExplorer.Views;
using GuidanceExplorerAddIn.Services;
using GuidanceExplorerAddIn.Properties;

namespace GuidanceExplorerAddIn.Controls
{

    public partial class GEGridView : UserControl
    {
        public GEGridView()
        {
            InitializeComponent();
        }
      
        private int NoteColumnIndex { get; set; }

        /// <summary>
        /// The current column collection
        /// </summary>
        private ColumnCollection _columns;

        /// <summary>
        /// The current checklist being displayed
        /// </summary>
        private Checklist _checklist;

        /// <summary>
        /// The items collection that is being displayed.
        /// </summary>
        private IGuidanceItemContainer _itemsContainer;

        /// <summary>
        /// Used to notify the number of rows in the list.
        /// </summary>
        private ToolStripStatusLabel _statusLabel;

        /// <summary>
        /// Keep the reference to the storage service.
        /// </summary>
        private IAddinModelStorageService _modelService;

        /// <summary>
        /// Whether the currently selected item is notified.
        /// </summary>
        private bool _disableSelectedEvent;

        /// <summary>
        /// Parent node of the contents showed on grid
        /// </summary>
        private TreeNode _parentNode;

        public IAddinModelStorageService ModelService
        {
            set { _modelService = value; }
        }

        //public GEController Controller { set;  private get; }

        public bool DisableSelectedEvent
        {
            set { _disableSelectedEvent = value; }
        }
        /// <summary>
        /// Used to notify the number of rows in the list.
        /// </summary>
        public ToolStripStatusLabel StatusLabel
        {
            set { _statusLabel = value; }
        }

        /// <summary>
        /// Parent node of the contents showed on grid
        /// </summary>
        public TreeNode ParentNode
        {
            set { _parentNode = value; }
            get { return _parentNode; }
        }

        #region Public Events

        public event EventHandler<GuidanceItemFileReferenceEventArgs> GuidanceItemOpened;
        public event EventHandler<GEListContextMenuEventArgs> OpeningContextMenuListView;

        #endregion

        public void ShowChecklist(Checklist checklist) 
        {
            _checklist = checklist;
            ShowContents(checklist, checklist.Columns);
        }

        public void RefreshCheckList()
        {
            ShowContents(_checklist, _checklist.Columns);
        }

        public void ClearDataGrid()
        {
            dataGridView.Rows.Clear();
        }
        /// <summary>
        /// Public operation called by the tree view for displaying contents of a container.
        /// </summary>
        /// <param name="itemsContainer">The items container instance.</param>
        public void ShowContents(IGuidanceItemContainer itemsContainer, ColumnCollection columnCollection)
        {
            try
            {
                dataGridView.Rows.Clear();

                //Use default column structure.
                if (itemsContainer.Columns == null || itemsContainer.Columns.Count == 0)
                {
                    if (columnCollection == null || columnCollection.Count == 0)
                    {
                        _columns = itemsContainer.Columns = ColumnCollection.DefaultColumns(GuidanceType.New(GuidanceType.DefaultTypeName));
                    }
                    else
                    {
                        _columns = columnCollection;
                    }
                }
                else
                {
                    _columns = itemsContainer.Columns;
                }

                //Prepare the columns in the list.
                PrepareColumns(_columns);

                //Keep the enumerator.
                _itemsContainer = itemsContainer;

                //Populate the list.
                PopulateList(itemsContainer, _columns);

            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
            finally
            {
                //list.EndUpdate();
            }

            if (_statusLabel != null)
            {
                _statusLabel.Text = string.Format("{0} items", dataGridView.Rows.Count);
            }
        }

        /// <summary>
        /// Public operation called by the tree view for displaying contents of a container.
        /// </summary>
        /// <param name="itemsContainer">The items container instance.</param>
        private void ShowFilteredContents(IGuidanceItemContainer itemsContainer, ColumnCollection columnCollection)
        {
            try
            {
                dataGridView.Rows.Clear();

                //Keep the enumerator.
                _itemsContainer = itemsContainer;

                //Populate the list.
                PopulateList(itemsContainer, _columns);

                //Set the sorter
                // list.ListViewItemSorter = new GuidanceItemListViewComparer(_columns);
            }
            catch (Exception e)
            {
                //TODO: remove this
                MessageBox.Show(e.ToString());
            }
            finally
            {
                //list.EndUpdate();
            }

            if (_statusLabel != null)
            {
                _statusLabel.Text = string.Format("{0} items", dataGridView.Rows.Count);
            }
        }

        public void SortGrid(string columnName, ListSortDirection direction)
        {
            try
            {
                DataGridViewColumn col = dataGridView.Columns[columnName];
                dataGridView.Sort(col, direction);
            }
            catch
            { }
        }
        /// <summary>
        /// Prepare the columns by removing the previous and creating new ones as defined in the new item container.
        /// </summary>
        /// <param name="columnCollection">The item container instance.</param>
        private void PrepareColumns(ColumnCollection columnCollection)
        {
            //Update the columns in the list
            dataGridView.Columns.Clear();
            
            SortedList<int, Column> columns = new SortedList<int, Column>(columnCollection.Count);
            
            foreach (Column column in columnCollection.Values)
            {
                column.AutoFilter = string.Empty;  
                columns.Add(column.Position, column);
            }

            //Use default column defined in the model.
            foreach (Column column in columns.Values)
            {
                //If the column should be enabled
                if (column.Visible)
                {
                    int colIndex;
                    if (column.Field!= null && column.Field.Type == FieldType.Check)
                    {
                        DataGridViewColumn col = new DataGridViewCheckBoxColumn();

                        DatagridViewCheckBoxHeaderCell chkHeader = new DatagridViewCheckBoxHeaderCell();
                        col.HeaderCell = chkHeader;
                        chkHeader.OnCheckBoxClicked += new CheckBoxClickedHandler(chkHeader_OnCheckBoxClicked);

                        col.Name = column.Source;
                        col.HeaderText = column.Caption;
                        colIndex = dataGridView.Columns.Add(col);
                    }
                    else if ("Note".Equals(column.Source)) //TODO: un-hardcode
                    {
                        //Notes column have special heading to clear all notes
                        DataGridViewColumn col = new DataGridViewTextBoxColumn();
                        DatagridViewNotesHeaderCell notesHeader = new DatagridViewNotesHeaderCell();
                        col.HeaderCell = notesHeader;
                        notesHeader.OnClearNotesClicked += new ClearNotesClickedHandler(notesHeader_OnClearNotesClicked);
                        
                        col.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
                        col.Name = column.Source;
                        col.HeaderText = column.Caption;
                        colIndex = dataGridView.Columns.Add(col);
                        NoteColumnIndex = colIndex;
                    }
                    else if (column.Field != null && column.Field.Type == FieldType.Combo)
                    {
                        DataGridViewColumn col = new DataGridViewTextBoxColumn();
                        DatagridViewFilterHeaderCell filterHeader = new DatagridViewFilterHeaderCell();
                        col.HeaderCell = filterHeader;
                        filterHeader.OnFilterColumnClicked += new EventHandler(filterHeader_OnFilterColumnClicked);
                        col.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
                        //col.SortMode = DataGridViewColumnSortMode.Programmatic;
                        col.Name = column.Source;
                        col.HeaderText = column.Caption;
                        col.Tag = column;
                        colIndex = dataGridView.Columns.Add(col);
                    }
                    else
                    {
                        
                        colIndex = dataGridView.Columns.Add(column.Source, column.Caption);
                    }
                    dataGridView.Columns[colIndex].ReadOnly = ! column.Editable;
                    
                   // SetColumnSortIcon(listColumn);
                }
            }
        }
        void OnSorting(object sender, EventArgs e)
        {

        }
        void filterHeader_OnFocusLost(object sender, EventArgs e)
        {
            filterValueList.Visible = false;
        }
        void filterHeader_OnFilterColumnClicked(object sender, EventArgs e)
        {
            DatagridViewFilterHeaderCell colHeader = sender as DatagridViewFilterHeaderCell;
            if (colHeader != null)
            {
                DataGridViewColumn clickedColumn = dataGridView.Columns[colHeader.ColumnIndex];
                
                
                //Clear the filter list.
                filterValueList.Items.Clear();
                
                filterValueList.LostFocus += new EventHandler(filterHeader_OnFocusLost);
                //Populate the filter list.
                foreach (string value in GetUniqueValues(clickedColumn))
                {
                    filterValueList.Items.Add(value);
                }
                 //Show the filter list
                filterValueList.Top = dataGridView.Top + colHeader.CellBounds.Height; //22; //Height of the header.
                filterValueList.Left = colHeader.CellBounds.Left;// Win32.GetScrollPos(dataGridView.Handle, 0) * -1; ;

                //filterValueList.Location =  dataGridView[clickedColumn.Index,0].ContentBounds.Location;// col HeaderCell .HeaderCell.ContentBounds.Location; 
                filterValueList.Visible = true;
                filterValueList.Focus();
                filterValueList.Tag = clickedColumn;

                //Refresh the data.
                //RefreshData();
            }
        }

        /// <summary>
        /// Called when an item is selected in the filter list box.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void filterValueList_SelectedIndexChanged(object sender, EventArgs e)
        {
            DataGridViewColumn clickedColumn = filterValueList.Tag as DataGridViewColumn;
            if (clickedColumn == null)
            {
                //The tag must be set.
                return;
            }
            if (filterValueList.SelectedIndex == 0)
            {
                ((Column)clickedColumn.Tag).AutoFilter = null;
            }
            else
            {
                string selectedValue = filterValueList.SelectedItem.ToString();

                //Reset Filters
                if (selectedValue.Equals(Resources.ResetFiltersGridHeaderText))
                {
                    foreach (Column column in _itemsContainer.Columns.Values)
                    {
                        column.AutoFilter = null;
                    }
                }
                else
                {
                    ((Column)clickedColumn.Tag).AutoFilter = (string)filterValueList.SelectedItem;
                }
            }

            //Update the column icon.
            //SetColumnSortIcon(clickedColumn);

            //Clear the filter listbox.
            filterValueList.Visible = false;
            filterValueList.Tag = null;

            //Recalculate the list.
            ShowFilteredContents(_itemsContainer, _itemsContainer.Columns);
        }



        /// <summary>
        /// Get a list with unique values in the list for a specific column.
        /// </summary>
        /// <param name="clickedColumn"></param>
        /// <returns></returns>
        private List<string> GetUniqueValues(DataGridViewColumn clickedColumn)
        {
            //Find the unique values.
            List<string> uniqueValues = new List<string>();

            //Iterate through all the items.
            foreach (GuidanceItem guidanceItem in _itemsContainer)
            {
                string value;
                GuidanceItemFileReference fileRef = guidanceItem as GuidanceItemFileReference;

                //If the item is a file reference it means it exists.
                if (fileRef == null)
                {
                    fileRef = _modelService.Guidance.GetGuidanceItemFileReference(guidanceItem);
                }

                value = fileRef[clickedColumn.Name];

                if (!String.IsNullOrEmpty(value) &&
                    !uniqueValues.Contains(value))
                {
                    uniqueValues.Add(value);
                }
            }

            //Sort the values.
            uniqueValues.Sort();

            //Add the remove filter marker.
            uniqueValues.Insert(0, "(all)");
            //Add the reset filters marker
            uniqueValues.Insert(1,Resources.ResetFiltersGridHeaderText);
            return uniqueValues;
        }


        void notesHeader_OnClearNotesClicked()
        {
            if (MessageBox.Show(AddinMessages.CONFIRM_CLEAR_ALL_NOTES, "", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                if (dataGridView.Rows.Count > 0)
                {
                    foreach (DataGridViewRow row in dataGridView.Rows)
                    {
                        row.Cells[NoteColumnIndex].Value = "";
                    }
                    //_checklist.ResetNotes();
                }
            }
        }
        void chkHeader_OnCheckBoxClicked(bool state)
        {
            foreach (DataGridViewRow row in dataGridView.Rows)
            {
                row.Cells[0].Value = state;
            }
        }


        /// <summary>
        /// Populate the list with the provided items
        /// </summary>
        /// <param name="itemsCollection"></param>
        private void PopulateList(IGuidanceItemContainer itemsCollection, ColumnCollection columnCollection)
        {
            //Keep track of the items added to the list to avoid duplicates.
            Dictionary<string, GuidanceItem> uniqueList = new Dictionary<string, GuidanceItem>();

            //The flag to detect whether any filter has been set.
            bool autoFilterSet = false;

            //Evaluate if an autofilter has been set.
            foreach (Column column in columnCollection.Values)
            {
                autoFilterSet |= !String.IsNullOrEmpty(column.AutoFilter);
                if (autoFilterSet)
                {
                    break;
                }
            }

            //Populate the list with the items.
            foreach (GuidanceItem item in itemsCollection)
            {
                GuidanceItemFileReference fileRef = item as GuidanceItemFileReference;

                if (fileRef == null)
                {
                    //If the guidance item is not a file reference, then it points to a file reference in a library.
                    fileRef = _modelService.Guidance.GetGuidanceItemFileReference(item);
                }
                if ((fileRef != null) &&
                    (!uniqueList.ContainsKey(fileRef.Id)) &&
                    (!autoFilterSet || EvaluateAutoFilter(fileRef, columnCollection)))
                {
                   // string[] values = GuidanceItemDataRow.GetSubItems(fileRef,columnCollection);
                    object[] values = GetItemValues(fileRef, columnCollection);
                    int newRowIndex = dataGridView.Rows.Add(values);
                    dataGridView.Rows[newRowIndex].Tag = fileRef;
                    //list.Rows.Add(GuidanceItemDataRow.New(fileRef,columnCollection));
                    //ListViewItem viewItem = list.Rows.Add( GuidanceItemListViewItem.New(fileRef, columnCollection));
                    uniqueList.Add(fileRef.Id, fileRef);
                    //if (checkBoxes && _checklist != null)
                    //{
                    //    viewItem.Checked = _checklist.IsChecked(item.Id);   
                    //}
                }
            }

            //If there are items, then select the first.
            if (dataGridView.Rows.Count != 0)
            {
                dataGridView.ClearSelection();
                dataGridView.Rows[0].Selected = true;
            }
            else
            {
                //TODO: update properties pane??

                ////There is no item selected, then clean the details view.
                //if (GuidanceItemSelected != null && !_disableSelectedEvent)
                //{
                //    GuidanceItemEventArgs eventArgs = new GuidanceItemEventArgs(null);
                //    GuidanceItemSelected(this, eventArgs);
                //}
                //else
                //{
                //    //Ignore to avoid duplicates.
                //}
            }
        }

        /// <summary>
        /// Evaluates the auto filter set in the columns
        /// </summary>
        /// <param name="fileRef"></param>
        /// <param name="columnCollection"></param>
        /// <returns></returns>
        private bool EvaluateAutoFilter(GuidanceItemFileReference fileRef, ColumnCollection columnCollection)
        {
            foreach (Column column in columnCollection.Values)
            {
                //If the autofilter has been set
                if (string.IsNullOrEmpty(column.AutoFilter))
                {
                    continue;
                }

                //Compares both strings.
                if (!string.Equals(fileRef[column.Source], column.AutoFilter, StringComparison.InvariantCultureIgnoreCase))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Occurs when an item in the list is double clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dataGridView_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            try
            {
                DataGridView.HitTestInfo info = dataGridView.HitTest(e.X, e.Y);
                if (info.RowIndex >= 0 && GuidanceItemOpened != null && dataGridView.SelectedRows.Count > 0)
                {
                    GuidanceItem item = dataGridView.SelectedRows[0].Tag as GuidanceItem;
                    if (item != null)
                    {
                        //GuidanceItemEventArgs eventArgs = new GuidanceItemEventArgs(item,_checklist);

                        GuidanceItemFileReference gifr = GetGuidanceItemFileReference(item);
                        GuidanceItemFileReferenceEventArgs eventArgs = new GuidanceItemFileReferenceEventArgs(new GuidanceItemFileReference[] { gifr }, ReadProtection.ReadOnly);

                        GuidanceItemOpened(this, eventArgs);
                    }
                }
                
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private object[] GetItemValues(GuidanceItem item, ColumnCollection columns)
        {
            PropertyDescriptorCollection propertyDescriptors = TypeDescriptor.GetProperties(item.GetType());

            SortedList<int, Column> sortedCols = new SortedList<int, Column>(columns.Count);
            foreach (Column column in columns.Values)
            {
                if (column.Visible)
                {
                    sortedCols.Add(column.Position, column);
                }
            }

            object[] values = new object[sortedCols.Count];
            int index = 0;

            foreach (Column column in sortedCols.Values)
            {
                object value = null;
                bool propertyFound = false;

                //Find the property in the type.
                PropertyDescriptor descriptor = propertyDescriptors[column.Source];
                if (descriptor != null)
                {
                    value = (string)descriptor.GetValue(item);
                    propertyFound = true;
                }

                //If the property was not found, then use the custom data.
                if (!propertyFound)
                {
                    value = item[column.Source];
                }

                if (_checklist != null)
                {
                    if (column.Source.Equals("Checked"))
                    {
                        value = _checklist.IsChecked(item.Id);
                    }
                    else if (column.Source.Equals("Note"))
                    {
                        value = _checklist.GetItemNotes(item.Id);
                    }
                }

                //Set the value of the array.
                values[index++] = value;
            }

            return values;
        }

        private void dataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            Debug.WriteLine("ValueChanged");
            object newValue = dataGridView[e.ColumnIndex, e.RowIndex].Value;
            GuidanceItem item = dataGridView.Rows[e.RowIndex].Tag as GuidanceItem;

            //Debug.WriteLine("New value: " + newValue.ToString());
            if (_checklist != null && newValue != null && item != null)
            {
                if (e.ColumnIndex == 0)
                {
                    _checklist.SetCheck(item.Id, (bool)newValue);
                }
                //If it is the note column set new note value in _checklist
                else if (dataGridView.Columns[e.ColumnIndex].Name.Equals("Note"))
                {
                    _checklist.SetItemNotes(item.Id, (string)newValue);
                }
            }
        }

        private void dataGridView_MouseDown(object sender, MouseEventArgs e)
        {
            //base.MouseDown(e);
            if (e.Button == MouseButtons.Left && e.Clicks == 1)
            {
				//ltmbpoint = e.Location;
				//lmbdown = true;

                DataGridView.HitTestInfo info = dataGridView.HitTest(e.X, e.Y);
                //Hack to allow clicking on the check ot note column instead if dragging
                if (_checklist!= null && ( info.ColumnIndex == 0 || info.ColumnIndex == NoteColumnIndex))
                {
                    return;
                }
                if (info.RowIndex >= 0)
				{
					//dragRow = dataGridView.Rows[info.RowIndex];

					// Begin DragDrop operation so the mulitselected rows keeps selected
					GuidanceItemCollection guidanceItems = GuidanceItemCollection.New();
					foreach (DataGridViewRow row in dataGridView.SelectedRows)
					{
						GuidanceItem guidanceItem = row.Tag as GuidanceItem;
						if (guidanceItem != null)
						{
							guidanceItems.Add(guidanceItem.Id, guidanceItem);
						}
					}
					dataGridView.DoDragDrop(guidanceItems, DragDropEffects.Link);
				}
				else
				{
					//dragRow = null;
				}
            }
            else if (e.Button == MouseButtons.Right && e.Clicks == 1)
            {
            }
        }

        /// <summary>
        /// Refresh the contents by loading the content into the list.
        /// </summary>
        /// <param name="item"></param>
        public void RefreshItems(GuidanceItem item)
        {
            dataGridView.Rows.Clear();
            PopulateList(_itemsContainer, _itemsContainer.Columns);
        }

        /// <summary>
        /// Get the GuidanceItemFileReference for this guidance.
        /// </summary>
        /// <param name="guidance"></param>
        /// <returns></returns>
        private GuidanceItemFileReference GetGuidanceItemFileReference(GuidanceItem item)
        {
            GuidanceItemFileReference gifr = item as GuidanceItemFileReference;
            if (gifr != null)
            {
                return gifr;
            }
            else
            {
                return _modelService.Guidance.GetGuidanceItemFileReference(item);
            }
        }

        private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
        {
            //If the event has been subscribed.
            if (OpeningContextMenuListView != null && dataGridView.SelectedRows.Count  > 0)
            {
                List<GuidanceItemFileReference> guidanceItems = new List<GuidanceItemFileReference>(dataGridView.SelectedRows.Count);
                foreach (DataGridViewRow row in dataGridView.SelectedRows)
                {
                    GuidanceItemFileReference guidanceItem = row.Tag as GuidanceItemFileReference;
                    if (guidanceItem != null)
                    {
                        guidanceItems.Add(guidanceItem);
                    }
                }
                OpeningContextMenuListView(sender, new GEListContextMenuEventArgs(e,guidanceItems.ToArray(),_parentNode));
            }
        }


    }

    public class GEListContextMenuEventArgs : EventArgs
    {
        public CancelEventArgs Cancel { get; private set; }
        public GuidanceItemFileReference[] SelectedItems { get; private set; }
        public TreeNode ParentNode { get; private set; }

        public GEListContextMenuEventArgs(CancelEventArgs cancelEventArgs, GuidanceItemFileReference[] items)
        {
            Cancel = cancelEventArgs;
            SelectedItems = items;
            ParentNode = null;
        }

        public GEListContextMenuEventArgs(CancelEventArgs cancelEventArgs, GuidanceItemFileReference[] items, TreeNode parent)
        {
            Cancel = cancelEventArgs;
            SelectedItems = items;
            ParentNode = parent;
        }
    }
}
